package cn.coisini.config;

import cn.coisini.security.config.WhiteListConfig;
import cn.coisini.security.exception.RestAuthenticationPoint;
import cn.coisini.security.exception.RestfulAccessDeniedHandler;
import cn.coisini.security.filter.JwtAuthenticationFilter;
import cn.coisini.security.filter.RateLimitingFilter;
import cn.coisini.security.handler.CustomUserCache;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;

/**
 * @author xiangshaw
 * Description: SecurityConfig全局配置类
 * 流程：
 * 提交用户名和密码-》自定义密码组件-》自定义用户对象-》创建方法根据用户名查询用户信息
 * -》自定义认证过滤器-》返回信息工具类-》认证解析过滤器-》配置用户认证全局信息
 */
@Log4j2
@Configuration
// 对web支持
@EnableWebSecurity
// 组件开发 https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
// 推翻 extends WebSecurityConfigurerAdapter的方式，未改之前WebSecurityConfigurerAdapter也是实现implements WebSecurityConfigurer<WebSecurity>方式，现在内部类已经废弃
// 对注解@PreAuthorize, @PostAuthorize, @PreFilter, @PostFilter生效
@EnableMethodSecurity
// 自动生成构造函数
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    private final RateLimitingFilter rateLimitingFilter;
    private final UserDetailsService userDetailsService;
    private final WhiteListConfig whiteListConfig;

    // 获取白名单
    public String[] getWhiteListUrl() {
        return whiteListConfig.getWhiteListUrls().toArray(new String[0]);
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(authorize -> authorize
                        // 放行登录白名单
                        .requestMatchers(getWhiteListUrl()).permitAll()
                        // 除以上接口，其它接口都需要认证
                        .anyRequest().authenticated()
                )
                .csrf(AbstractHttpConfigurer::disable)
                // 禁用session
                .sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
                // 禁用frameOptions
                .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
                // 身份认证过滤器
                .authenticationProvider(authenticationProvider())
                // 自定义认证过滤器
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                // 接口限流
                .addFilterBefore(rateLimitingFilter, UsernamePasswordAuthenticationFilter.class)
                // 认证失败处理
                .exceptionHandling(exception -> exception
                        // 认证失败处理
                        .authenticationEntryPoint(new RestAuthenticationPoint())
                        // 授权失败处理
                        .accessDeniedHandler(new RestfulAccessDeniedHandler())
                );
        return http.build();
    }

    /**
     * 缓存用户信息
     */
    @Bean
    public CustomUserCache userCache() {
        return new CustomUserCache();
    }

    /**
     * 身份认证提供者
     */
    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        // 设置用户信息
        authProvider.setUserDetailsService(userDetailsService);
        // 密码加密
        authProvider.setPasswordEncoder(new MyPasswordEncoderConfig().passwordEncoder());
        // 缓存用户信息
        authProvider.setUserCache(userCache());
        return authProvider;
    }

    /**
     * 身份认证管理器 注入AuthenticationManager
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }
}
